﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Windows;
using System.Windows.Input;
using HashCalculator.Commands;
using HashCalculator.HashFunctions;

namespace HashCalculator.ViewModels
{
    class HashCalculatorViewModel : NotifyObject
    {
        public IEnumerable<HashModel> Models { get; private set; }

        public HashCalculatorViewModel()
        {
            ComputeCommand = new DelegateCommand(Compute, CanCompute);
            CancelCommand = new DelegateCommand(Cancel, CanCancel);
            CopyCommand = new DelegateCommand(Copy, CanCopy);
            BrowseCommand = new DelegateCommand(Browse, CanBrowse);

            //init models
            var SHA1 = new HashModel(new SHA1Managed(), "SHA1");
            SHA1.PropertyChanged += new PropertyChangedEventHandler(model_PropertyChanged);

            var MD5 = new HashModel(new MD5CryptoServiceProvider(), "MD5");
            MD5.PropertyChanged += new PropertyChangedEventHandler(model_PropertyChanged);

            var CRC32 = new HashModel(new CRC32(), "CRC32");
            CRC32.PropertyChanged += new PropertyChangedEventHandler(model_PropertyChanged);

            Models = new[] { SHA1, MD5, CRC32 };
        }

        void model_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == ExtractPropertyName(() => IsBusy))
            {
                RaisePropertyChanged(() => Result);
                RaisePropertyChanged(() => IsBusy);
                CommandManager.InvalidateRequerySuggested();
            }

            if ((sender as HashModel).Algorithm is SHA1Managed)
            {
                ProgressValue = (sender as HashModel).DonePercent / 100.0;
            }
        }

        string filePath;

        public string FilePath
        {
            get
            {
                return filePath;
            }
            set
            {
                filePath = value;
                RaisePropertyChanged(() => FilePath);
                CommandManager.InvalidateRequerySuggested();
            }
        }

        public bool IsBusy
        {
            get
            {
                return Models.Any(m => m.IsBusy);
            }
        }

        public string Result
        {
            get
            {
                return string.Join(Environment.NewLine,
                    Models.Select(m => string.Format("{0}: {1}", m.Name, m.Result)).ToArray());
            }
        }

        double progressValue;

        public double ProgressValue
        {
            get
            {
                return progressValue;
            }
            private set
            {
                progressValue = value;
                RaisePropertyChanged(() => ProgressValue);
            }
        }

        #region ComputeCommand

        public DelegateCommand ComputeCommand { get; private set; }

        void Compute()
        {
            if (File.Exists(filePath))
            {
                foreach (var item in Models)
                {
                    item.Compute(filePath);
                }
            }
            else
            {
                FilePath = null;
            }
            Clear();
        }

        bool CanCompute()
        {
            return FilePath != null && File.Exists(FilePath) && !IsBusy;
        }

        #endregion

        #region CancelCommand

        public DelegateCommand CancelCommand { get; private set; }

        void Cancel()
        {
            foreach (var item in Models)
            {
                item.Cancel();
            }
            Clear();
        }

        bool CanCancel()
        {
            return IsBusy;
        }

        #endregion

        #region CopyCommand

        public DelegateCommand CopyCommand { get; private set; }

        void Copy()
        {
            Clipboard.SetText(Result);
        }

        bool CanCopy()
        {
            return Models.All(m => m.Result != null);
        }

        #endregion

        #region BrowseCommand

        public DelegateCommand BrowseCommand { get; private set; }

        void Browse()
        {
            if (BrowseExecuted != null)
            {
                BrowseExecuted(this, EventArgs.Empty);
            }
        }

        bool CanBrowse()
        {
            return !IsBusy;
        }

        #endregion

        void Clear()
        {
            foreach (var item in Models)
            {
                item.Clear();
            }
            RaisePropertyChanged(() => Result);
        }

        public event EventHandler<EventArgs> BrowseExecuted;
    }
}
